# src/vmctrl.py

import os
import time
import tempfile
import subprocess


class VmwareController:
    def __init__(self, vmrun: str, vmx: str, vm_user: str, vm_passwd: str):
        if not os.path.isfile(vmrun):
            raise FileNotFoundError(f"{vmrun} not found.")
        self.vmrun = vmrun
        if not os.path.isfile(vmx):
            raise FileNotFoundError(f"{vmx} not found.")
        self.vmx = vmx
        self.vm_prefix = [self.vmrun, "-T", "ws", "-gu", vm_user, "-gp", vm_passwd]

        # determine creationflags for subprocesses on Windows to avoid creating new consoles
        if os.name == "nt":
            # CREATE_NO_WINDOW suppresses console windows for child processes
            self._creationflags = subprocess.CREATE_NO_WINDOW
        else:
            self._creationflags = 0

        self.start_vm()
        if not self.wait_for_vm_running():
            raise RuntimeError("Failed to start VM.")

    def _run_cmd(self, cmd, capture_output=False, text=True):
        """Run subprocess with consistent flags (Windows: no new console)."""
        kwargs = {
            "shell": False,
            "stdin": subprocess.DEVNULL,
            "creationflags": self._creationflags,
        }
        if capture_output:
            kwargs.update({"capture_output": True, "text": text, "encoding": "utf-8"})
        return subprocess.run(cmd, **kwargs)

    def start_vm(self):
        """Start virtual machine"""
        cmd = [self.vmrun, "-T", "ws", "start", self.vmx]
        self._run_cmd(cmd, capture_output=False)

    def stop_vm(self):
        """Stop virtual machine"""
        cmd = [self.vmrun, "-T", "ws", "stop", self.vmx, "hard"]
        self._run_cmd(cmd, capture_output=False)

    def wait_for_vm_running(self, timeout=120):
        """Wait for virtual machine to run"""
        for _ in range(timeout):
            cmd = [self.vmrun, "-T", "ws", "list"]
            result = self._run_cmd(cmd, capture_output=True)
            if f"{self.vmx}" in result.stdout:
                return True
            time.sleep(1)
        return False

    # def snapshot(self, name: str):
    #     """Create snapshot"""
    #     cmd = [self.vmrun, "-T", "ws", "snapshot", self.vmx, name]
    #     result = self._run_cmd(cmd, capture_output=False)
    #     if result.returncode != 0:
    #         return f"Error: {result.stderr}\nStdout: {result.stdout}"
    #     return result.stdout

    def run_program(self, program_path: str, *args) -> str:
        """Run program in virtual machine."""
        cmd = [*self.vm_prefix, "runProgramInGuest", self.vmx, program_path, *args]
        result = self._run_cmd(cmd, capture_output=True)
        if result.returncode != 0:
            return f"Error: {result.stderr}\nStdout: {result.stdout}"
        return result.stdout

    def run_script(self, script: str, *args) -> str:
        """Run script in virtual machine."""
        cmd = [*self.vm_prefix, "runScriptInGuest", self.vmx, script, *args]
        result = self._run_cmd(cmd, capture_output=True)
        return result.stdout

    def is_file_exists(self, file_path: str) -> bool:
        """Check if file exists."""
        cmd = [*self.vm_prefix, "fileExistsInGuest", self.vmx, file_path]
        result = self._run_cmd(cmd, capture_output=True)
        return "file exists" in result.stdout.strip()

    def is_dir_exists(self, dir_path: str) -> bool:
        """Check if directory exists."""
        cmd = [*self.vm_prefix, "directoryExistsInGuest", self.vmx, dir_path]
        result = self._run_cmd(cmd, capture_output=True)
        return "directory exists" in result.stdout.strip()

    def create_temp_file(self) -> str:
        """Create temporary file."""
        cmd = [*self.vm_prefix, "createTempFileInGuest", self.vmx]
        result = self._run_cmd(cmd, capture_output=True)
        return result.stdout

    def delete_file(self, file_path: str) -> str:
        """Delete file."""
        cmd = [*self.vm_prefix, "deleteFileInGuest", self.vmx, file_path]
        result = self._run_cmd(cmd, capture_output=True)
        return result.stdout

    def create_dir(self, dir_path: str) -> str:
        """Create directory."""
        cmd = [*self.vm_prefix, "createDirectoryInGuest", self.vmx, dir_path]
        result = self._run_cmd(cmd, capture_output=True)
        return result.stdout

    def write_file(self, file_path: str, content: str, crlf: bool = False) -> str:
        """Write file content."""
        content = content.replace("\n", "\r\n") if crlf else content
        with tempfile.NamedTemporaryFile(delete=False) as f:
            f.write(content.encode("utf-8"))

        result = self.copy_file_from_host_to_guest(f.name, file_path)
        os.unlink(f.name)
        return result

    def delete_dir(self, dir_path: str) -> str:
        """Delete directory."""
        cmd = [*self.vm_prefix, "deleteDirectoryInGuest", self.vmx, dir_path]
        result = self._run_cmd(cmd, capture_output=True)
        return result.stdout

    def list_dir(self, dir_path: str) -> list[str]:
        """List files and subdirectories in directory."""
        cmd = [*self.vm_prefix, "listDirectoryInGuest", self.vmx, dir_path]
        result = self._run_cmd(cmd, capture_output=True)
        lines = result.stdout.strip().split("\n")
        return lines[1:] if len(lines) > 1 else []

    def copy_file_from_host_to_guest(self, host_path: str, guest_path: str) -> str:
        """Copy file from host to virtual machine."""
        cmd = [
            *self.vm_prefix,
            "CopyFileFromHostToGuest",
            self.vmx,
            host_path,
            guest_path,
        ]
        result = self._run_cmd(cmd, capture_output=True)
        if result.returncode != 0:
            return f"Error: {result.stderr}\nStdout: {result.stdout}"
        return result.stdout

    def copy_file_from_guest_to_host(self, guest_path: str, host_path: str) -> str:
        """Copy file from virtual machine to host."""
        cmd = [
            *self.vm_prefix,
            "CopyFileFromGuestToHost",
            self.vmx,
            guest_path,
            host_path,
        ]
        result = self._run_cmd(cmd, capture_output=True)
        return result.stdout

    def copy_dir_from_host_to_guest(self, host_path: str, guest_path: str) -> str:
        """Copy dir from host to virtual machine recursively."""
        if not os.path.exists(host_path):
            return f"Error: No found source dir {host_path} in host"

        if not os.path.isdir(host_path):
            return f"Error: {host_path} not a directory"

        # Check if target directory exists in guest, delete if exists
        if self.is_dir_exists(guest_path):
            self.delete_dir(guest_path)

        # Create target directory
        result = self.create_dir(guest_path)
        if result and "Error" in result:
            return result

        # Traverse all files and subdirectories in source directory
        for root, dirs, files in os.walk(host_path):
            # Calculate relative path
            rel_path = os.path.relpath(root, host_path)
            if rel_path == ".":
                guest_root = guest_path
            else:
                guest_root = os.path.join(guest_path, rel_path).replace("/", "\\")

            # Create subdirectories
            for dir_name in dirs:
                guest_dir_path = os.path.join(guest_root, dir_name).replace("/", "\\")
                # Check if directory already exists, delete if exists
                if self.is_dir_exists(guest_dir_path):
                    self.delete_dir(guest_dir_path)

                result = self.create_dir(guest_dir_path)
                if result and "Error" in result:
                    return result

            # Copy files
            for file_name in files:
                host_file_path = os.path.join(root, file_name)
                guest_file_path = os.path.join(guest_root, file_name).replace("/", "\\")
                result = self.copy_file_from_host_to_guest(
                    host_file_path, guest_file_path
                )
                if result and "Error" in result:
                    return result

        return ""

    def copy_dir_from_guest_to_host(self, guest_path: str, host_path: str) -> str:
        """Copy dir from virtual machine to host recursively."""
        # Check if guest directory exists
        if not self.is_dir_exists(guest_path):
            return f"Error: No found source dir {guest_path} in guest"

        # Create target directory
        if not os.path.exists(host_path):
            os.makedirs(host_path)
        elif not os.path.isdir(host_path):
            return f"Error: {host_path} not a directory"

        # Get directory contents
        items = self.list_dir(guest_path)

        for item in items:
            guest_item_path = os.path.join(guest_path, item).replace("/", "\\")
            host_item_path = os.path.join(host_path, item)

            # Check if it's a file or directory
            if self.is_dir_exists(guest_item_path):
                # Recursively copy subdirectory
                result = self.copy_dir_from_guest_to_host(
                    guest_item_path, host_item_path
                )
                if result and "Error" in result:
                    return result
            else:
                # Copy file
                result = self.copy_file_from_guest_to_host(
                    guest_item_path, host_item_path
                )
                if result and "Error" in result:
                    return result

        return ""
